#include "ce_core.h"

static const int ce_chain_nalloc = 4096;

static ce_pool_t *ce_chain_pool;
static ce_pool_t *ce_chain_buf_pool;
static size_t ce_chain_buf_size;

void
ce_chain_pool_init(size_t size) {
    if (ce_chain_pool != NULL) {
        return;
    }

    ce_chain_pool = ce_create_pool(sizeof(ce_chain_t), ce_chain_nalloc);
    ce_chain_buf_pool = ce_create_pool(sizeof(ce_chain_t) + size, ce_chain_nalloc);
    ce_chain_buf_size = size;
}

ce_chain_t *
ce_chain_get(ce_chain_t **head, ce_buf_t *buf) {
    ce_chain_t         *ch;

    if (ce_chain_pool == NULL) {
        return NULL;
    }

    if (buf != NULL) {
        ch = (ce_chain_t *)ce_pool_get(ce_chain_pool);

        // ch->buf = buf;
        ch->in_buf.start = buf->start;
        ch->in_buf.end = buf->end;
        ch->in_buf.pos = buf->pos;
        ch->in_buf.last = buf->last;
    } else {
        ch = (ce_chain_t *)ce_pool_get(ce_chain_buf_pool);

        // ch->buf = NULL;
        buf = &ch->in_buf;
        ch->in_buf.pos = buf->data;
        ch->in_buf.last = buf->data;
        ch->in_buf.start = buf->data;
        ch->in_buf.end = buf->data + ce_chain_buf_size;
    }

    ch->next = NULL;
    ch->ref = 1;

    if (*head == NULL) {
        *head = ch;

        ch->tail = ch;
    } else {
        (*head)->tail->next = ch;
        (*head)->tail = ch;
    }

    return ch;
}

ce_chain_t *
ce_chain_put(ce_chain_t *ch) {
    ce_chain_t         *next;

    ch->ref--;
    if (ch->ref != 0) {
        return NULL;
    }

    next = ch->next;

    if (ch->in_buf.start != ch->in_buf.data) {
    // if (ch->buf == NULL) {
        ce_pool_put(ce_chain_pool, ch);
    } else {
        ce_pool_put(ce_chain_buf_pool, ch);
    }

    return next;
}

void
ce_chain_acquire(ce_chain_t *head) {
    head->ref++;
}

void
ce_chain_put_all(ce_chain_t *head) {
    ce_chain_t         *ch;

    head->ref--;
    if (head->ref != 0) {
        return;
    }

    // TODO optimize, put chain once
    for (ch = head; ch != NULL; ch = head) {
        head = head->next;

        if (ch->in_buf.start != ch->in_buf.data) {
        // if (ch->buf == NULL) {
            ce_pool_put(ce_chain_pool, ch);
        } else {
            ce_pool_put(ce_chain_buf_pool, ch);
        }
    }
}
